home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTTP.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  25.4 KB  |  853 lines

  1. /*    HyperText Tranfer Protocol    - Client implementation        HTTP.c
  2. **    ==========================
  3. ** Modified:
  4. ** 27 Jan 1994  PDM  Added Ari Luotonen's Fix for Reload when using proxy
  5. **                   servers.
  6. */
  7.  
  8. #include "HTUtils.h"
  9. #include "tcp.h"
  10.  
  11. #include "HTTP.h"
  12.  
  13. #define HTTP_VERSION    "HTTP/1.0"
  14.  
  15. #define INIT_LINE_SIZE        1024    /* Start with line buffer this big */
  16. #define LINE_EXTEND_THRESH    256    /* Minimum read size */
  17. #define VERSION_LENGTH         20    /* for returned protocol version */
  18.  
  19. #include "HTParse.h"
  20. #include "HTTCP.h"
  21. #include "HTFormat.h"
  22. #include "HTFile.h"
  23. #include <ctype.h>
  24. #include "HTAlert.h"
  25. #include "HTMIME.h"
  26. #include "HTML.h"
  27. #include "HTInit.h"
  28. #include "HTAABrow.h"
  29.  
  30. #include "LYLeaks.h"
  31.  
  32. #ifdef NO_BCOPY
  33. #define bcopy(s, d, n) memcpy((d), (s), (n))
  34. #endif /* NO_BCOPY */
  35.  
  36. /* #define TRACE 1 */
  37.  
  38. struct _HTStream 
  39. {
  40.   HTStreamClass * isa;
  41. };
  42.  
  43. extern char * HTAppName;    /* Application name: please supply */
  44. extern char * HTAppVersion;    /* Application version: please supply */
  45. extern char * personal_mail_address;    /* User's name/email address */
  46.  
  47. extern BOOL using_proxy;    /* Are we using an HTTP gateway? */
  48. PUBLIC BOOL reloading = FALSE;    /* Reloading => send no-cache pragma to proxy */
  49.  
  50. extern char LYUserSpecifiedURL; /* Is the URL a goto? */
  51.  
  52. BOOL do_head = FALSE;        /* Whether or not we should do a head */
  53.  
  54. /*        Load Document from HTTP Server            HTLoadHTTP()
  55. **        ==============================
  56. **
  57. **    Given a hypertext address, this routine loads a document.
  58. **
  59. **
  60. ** On entry,
  61. **    arg    is the hypertext reference of the article to be loaded.
  62. **
  63. ** On exit,
  64. **    returns    >=0    If no error, a good socket number
  65. **        <0    Error.
  66. **
  67. **    The socket must be closed by the caller after the document has been
  68. **    read.
  69. **
  70. */
  71. PUBLIC int HTLoadHTTP ARGS4 (
  72.     CONST char *,         arg,
  73.     HTParentAnchor *,    anAnchor,
  74.     HTFormat,        format_out,
  75.     HTStream*,        sink)
  76. {
  77.   int s;                /* Socket number for returned data */
  78.   char *command;            /* The whole command */
  79.   char *eol;            /* End of line if found */
  80.   char *start_of_data;        /* Start of body of reply */
  81.   int status;                /* tcp return */
  82.   int bytes_already_read;
  83.   char crlf[3];            /* A CR LF equivalent string */
  84.   HTStream *target;        /* Unconverted data */
  85.   HTFormat format_in;            /* Format arriving in the message */
  86.   int do_post = 0;        /* ARE WE posting ? */
  87.   
  88.   BOOL had_header;        /* Have we had at least one header? */
  89.   char *line_buffer;
  90.   char *line_kept_clean;
  91.   BOOL extensions;        /* Assume good HTTP server */
  92.   int compressed;
  93.   char line[256];
  94.   
  95.   int length, doing_redirect, rv;
  96.   int already_retrying = 0;
  97.  
  98.   if(anAnchor->post_data)
  99.       do_post=TRUE;
  100.   
  101.   if (!arg)
  102.     {
  103.       status = -3;
  104.       _HTProgress ("Bad request.");
  105.       goto done;
  106.     }
  107.   if (!*arg) 
  108.     {
  109.       status = -2;
  110.       _HTProgress ("Bad request.");
  111.       goto done;
  112.     }
  113.   
  114.   sprintf(crlf, "%c%c", CR, LF);
  115.  
  116.   /* At this point, we're talking HTTP/1.0. */
  117.   extensions = YES;
  118.  
  119.  try_again:
  120.   /* All initializations are moved down here from up above,
  121.      so we can start over here... */
  122.   eol = 0;
  123.   bytes_already_read = 0;
  124.   had_header = NO;
  125.   length = 0;
  126.   doing_redirect = 0;
  127.   compressed = 0;
  128.   target = NULL;
  129.   line_buffer = NULL;
  130.   line_kept_clean = NULL;
  131.  
  132.   status = HTDoConnect (arg, "HTTP", TCP_PORT, &s);
  133.   if (status == HT_INTERRUPTED)
  134.     {
  135.       /* Interrupt cleanly. */
  136.       if (TRACE)
  137.         fprintf (stderr,
  138.                  "HTTP: Interrupted on connect; recovering cleanly.\n");
  139.       _HTProgress ("Connection interrupted.");
  140.       /* status already == HT_INTERRUPTED */
  141.       goto done;
  142.     }
  143.   if (status < 0) 
  144.     {
  145.       if (TRACE) 
  146.         fprintf(stderr, 
  147.                 "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, SOCKET_ERRNO);
  148.       HTAlert("Unable to connect to remote host.");
  149.       status = HT_NO_DATA;
  150.       goto done;
  151.     }
  152.   
  153.   /*    Ask that node for the document,
  154.    **    omitting the host name & anchor
  155.    */        
  156.   {
  157.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  158.     command = malloc(5 + strlen(p1)+ 2 + 31 + 
  159.         /* Referer: field */
  160.         (LYUserSpecifiedURL ? 0 : 
  161.                 (strlen((char *)HTLoadedDocumentURL()) + 10)));
  162.  
  163.     if (do_post)
  164.       strcpy(command, "POST ");
  165.     else if(do_head)
  166.     strcpy(command, "HEAD ");
  167.     else
  168.       strcpy(command, "GET ");
  169.  
  170.     /* if we are using a proxy gateway don't copy in the first slash
  171.      * of say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj
  172.      * so that just gopher://.... is sent.
  173.      */
  174.     if(using_proxy)
  175.         strcat(command, p1+1);
  176.     else
  177.         strcat(command, p1);
  178.     free(p1);
  179.   }
  180.   if (extensions) 
  181.     {
  182.       strcat(command, " ");
  183.       strcat(command, HTTP_VERSION);
  184.     }
  185.   
  186.   strcat(command, crlf);    /* CR LF, as in rfc 977 */
  187.   
  188.   if (extensions) 
  189.     {
  190.       int n, i;
  191.       extern char * language; /* Lynx's preferred language - FM */
  192.       
  193.       if (!HTPresentations) HTFormatInit();
  194.       n = HTList_count(HTPresentations);
  195.       
  196.       for(i=0; i<n; i++) 
  197.         {
  198.           HTPresentation * pres = HTList_objectAt(HTPresentations, i);
  199.           if (pres->rep_out == WWW_PRESENT)
  200.             {
  201.           if(pres->rep == WWW_SOURCE) 
  202.           sprintf(line, "Accept: */*%c%c", CR, LF);
  203.             else
  204.                   sprintf(line, "Accept: %s%c%c", 
  205.                     HTAtom_name(pres->rep), CR, LF);
  206.               StrAllocCat(command, line);
  207.             }
  208.         }
  209.  
  210.       if (language) {
  211.       sprintf(line, "Accept-Language: %s; q=1%c%c", language, CR, LF);
  212.       StrAllocCat(command, line);
  213.       sprintf(line, "Accept-Language: *; q=0.1%c%c", CR, LF);
  214.       StrAllocCat(command, line);
  215.       }
  216.  
  217.       /*
  218.        * When reloading give no-cache pragma to proxy server to make
  219.        * it refresh its cache. -- Ari L. <luotonen@dxcern.cern.ch>
  220.        */
  221.       if (reloading) {
  222.           sprintf(line, "Pragma: no-cache%c%c", CR, LF);
  223.           StrAllocCat(command, line);
  224.       }
  225.       reloading = FALSE;           /* Now turn it off again if on */
  226.  
  227.       sprintf(line, "User-Agent:  %s/%s  libwww/%s%c%c",
  228.               HTAppName ? HTAppName : "unknown",
  229.               HTAppVersion ? HTAppVersion : "0.0",
  230.               HTLibraryVersion, CR, LF);
  231.       StrAllocCat(command, line);
  232.  
  233.       if(personal_mail_address) {
  234.           sprintf(line, "From:  %s%c%c", personal_mail_address, CR,LF);
  235.           StrAllocCat(command, line);
  236.       }
  237.  
  238.       if(!LYUserSpecifiedURL) {
  239.           StrAllocCat(command, "Referer:  ");
  240.           StrAllocCat(command, (char *)HTLoadedDocumentURL());
  241.           sprintf(line, "%c%c", CR, LF);
  242.           StrAllocCat(command, line);
  243.       }
  244.       
  245.       {
  246.         char *docname;
  247.         char *hostname;
  248.         char *colon;
  249.         int portnumber;
  250.         char *auth;
  251.         
  252.         docname = HTParse(arg, "", PARSE_PATH);
  253.         hostname = HTParse(arg, "", PARSE_HOST);
  254.         if (hostname &&
  255.             NULL != (colon = strchr(hostname, ':'))) 
  256.           {
  257.             *(colon++) = '\0';    /* Chop off port number */
  258.             portnumber = atoi(colon);
  259.           }
  260.         else portnumber = 80;
  261.         
  262.         if (NULL!=(auth=HTAA_composeAuth(hostname, portnumber, docname))) 
  263.           {
  264.             sprintf(line, "%s%c%c", auth, CR, LF);
  265.             StrAllocCat(command, line);
  266.           }
  267.         if (TRACE) 
  268.           {
  269.             if (auth)
  270.               fprintf(stderr, "HTTP: Sending authorization: %s\n", auth);
  271.             else
  272.               fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
  273.           }
  274.         FREE(hostname);
  275.         FREE(docname);
  276.       }
  277.     }
  278.  
  279.   if (do_post)
  280.     {
  281.       if (TRACE)
  282.         fprintf (stderr, "HTTP: Doing post, content-type '%s'\n",
  283.                  anAnchor->post_content_type);
  284.       sprintf (line, "Content-type: %s%c%c",
  285.                anAnchor->post_content_type ? anAnchor->post_content_type 
  286.                             : "lose", CR, LF);
  287.       StrAllocCat(command, line);
  288.       {
  289.         int content_length;
  290.         if (!anAnchor->post_data)
  291.           content_length = 4; /* 4 == "lose" :-) */
  292.         else
  293.           content_length = strlen (anAnchor->post_data);
  294.         sprintf (line, "Content-length: %d%c%c",
  295.                  content_length, CR, LF);
  296.         StrAllocCat(command, line);
  297.       }
  298.       
  299.       StrAllocCat(command, crlf);    /* Blank line means "end" */
  300.       
  301.       StrAllocCat(command, anAnchor->post_data);
  302.     }
  303.  
  304.   StrAllocCat(command, crlf);    /* Blank line means "end" */
  305.   
  306.   if (TRACE)
  307.     fprintf (stderr, "Writing:\n%s----------------------------------\n",
  308.              command);
  309.   
  310.   _HTProgress ("Sending HTTP request.");
  311.  
  312.   status = NETWRITE(s, command, (int)strlen(command));
  313.   free (command);
  314.   if (status <= 0) 
  315.     {
  316.       if (status == 0)
  317.         {
  318.           if (TRACE)
  319.             fprintf (stderr, "HTTP: Got status 0 in initial write\n");
  320.           /* Do nothing. */
  321.         }
  322.       else if 
  323.         ((SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) &&
  324.          !already_retrying &&
  325.          /* Don't retry if we're posting. */ !do_post)
  326.           {
  327.             /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  328.             if (TRACE)
  329.               fprintf 
  330.                 (stderr, 
  331.                  "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n");
  332.             _HTProgress ("Retrying as HTTP0 request.");
  333.             NETCLOSE(s);
  334.             extensions = NO;
  335.             already_retrying = 1;
  336.             goto try_again;
  337.           }
  338.       else
  339.         {
  340.           if (TRACE)
  341.             fprintf (stderr, "HTTP: Hit unexpected network WRITE error; aborting connection.\n");
  342.           NETCLOSE (s);
  343.           status = -1;
  344.           HTAlert("Unexpected network write error; connection aborted.");
  345.           goto done;
  346.         }
  347.     }
  348.   
  349.   if (TRACE)
  350.     fprintf (stderr, "HTTP: WRITE delivered OK\n");
  351.   _HTProgress ("HTTP request sent; waiting for response.");
  352.  
  353.   /*    Read the first line of the response
  354.    **    -----------------------------------
  355.    */
  356.   
  357.   {
  358.     /* Get numeric status etc */
  359.     BOOL end_of_file = NO;
  360.     HTAtom * encoding = HTAtom_for("8bit");
  361.     int buffer_length = INIT_LINE_SIZE;
  362.     
  363.     line_buffer = (char *) malloc(buffer_length * sizeof(char));
  364.     
  365.     do 
  366.       {    /* Loop to read in the first line */
  367.         /* Extend line buffer if necessary for those crazy WAIS URLs ;-) */
  368.         if (buffer_length - length < LINE_EXTEND_THRESH) 
  369.           {
  370.             buffer_length = buffer_length + buffer_length;
  371.             line_buffer = 
  372.               (char *) realloc(line_buffer, buffer_length * sizeof(char));
  373.           }
  374.         if (TRACE)
  375.           fprintf (stderr, "HTTP: Trying to read %d\n",
  376.                    buffer_length - length - 1);
  377.         status = NETREAD(s, line_buffer + length,
  378.                          buffer_length - length - 1);
  379.         if (TRACE)
  380.           fprintf (stderr, "HTTP: Read %d\n", status);
  381.         if (status <= 0) 
  382.           {
  383.             /* Retry if we get nothing back too; 
  384.                bomb out if we get nothing twice. */
  385.             if (status == HT_INTERRUPTED)
  386.               {
  387.                 if (TRACE)
  388.                   fprintf (stderr, "HTTP: Interrupted initial read.\n");
  389.                 _HTProgress ("Connection interrupted.");
  390.                 status = HT_INTERRUPTED;
  391.                 goto clean_up;
  392.               }
  393.             else if 
  394.               (status < 0 &&
  395.                (SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || 
  396.              SOCKET_ERRNO == EPIPE) && !already_retrying && !do_post)
  397.               {
  398.                 /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  399.                 if (TRACE)
  400.                   fprintf (stderr, "HTTP: BONZO Trying again with HTTP0 request.\n");
  401.                 NETCLOSE(s);
  402.                 if (line_buffer) 
  403.                   free(line_buffer);
  404.                 if (line_kept_clean) 
  405.                   free(line_kept_clean);
  406.                 
  407.                 extensions = NO;
  408.                 already_retrying = 1;
  409.                 _HTProgress ("Retrying as HTTP0 request.");
  410.                 goto try_again;
  411.               }
  412.             else
  413.               {
  414.                 if (TRACE)
  415.                   fprintf (stderr, "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", status);
  416.                 HTAlert("Unexpected network read error; connection aborted.");
  417.  
  418.                 NETCLOSE (s);
  419.                 status = -1;
  420.                 goto clean_up;
  421.               }
  422.           }
  423.  
  424.         bytes_already_read += status;
  425.         {
  426.           char line[256];
  427.           sprintf (line, "Read %d bytes of data.", bytes_already_read);
  428.           HTProgress (line);
  429.         }
  430.         
  431. #ifdef UCX  /* UCX returns -1 on EOF */
  432.         if (status == 0 || status == -1) 
  433. #else
  434.         if (status == 0)
  435. #endif
  436.           {
  437.             end_of_file = YES;
  438.             break;
  439.           }
  440.         line_buffer[length+status] = 0;
  441.         
  442.         if (line_buffer)
  443.           {
  444.             if (line_kept_clean)
  445.               free (line_kept_clean);
  446.             line_kept_clean = (char *)malloc (buffer_length * sizeof (char));
  447.             bcopy (line_buffer, line_kept_clean, buffer_length);
  448.           }
  449.         
  450.         eol = strchr(line_buffer + length, LF);
  451.         /* Do we *really* want to do this? */
  452.         if (eol && eol != line_buffer && *(eol-1) == CR) 
  453.           *(eol-1) = ' '; 
  454.         
  455.         length = length + status;
  456.  
  457.         /* Do we really want to do *this*? */
  458.         if (eol) 
  459.           *eol = 0;        /* Terminate the line */
  460.       }
  461.     /* All we need is the first line of the response.  If it's a HTTP/1.0
  462.        response, then the first line will be absurdly short and therefore
  463.        we can safely gate the number of bytes read through this code
  464.        (as opposed to below) to ~1000. */
  465.     /* Well, let's try 100. */
  466.     while (!eol && !end_of_file && bytes_already_read < 100);
  467.   } /* Scope of loop variables */
  468.     
  469.     
  470.   /*    We now have a terminated unfolded line. Parse it.
  471.    **    -------------------------------------------------
  472.    */
  473.   if (TRACE)
  474.     fprintf(stderr, "HTTP: Rx: %s\n", line_buffer);
  475.   
  476. /* Kludge to work with old buggy servers and the VMS Help gateway.
  477. ** They can't handle the third word, so we try again without it.
  478. */
  479.   if (extensions &&       /* Old buggy server or Help gateway? */
  480.       (0==strncmp(line_buffer,"<TITLE>Bad File Request</TITLE>",31) ||
  481.        0==strncmp(line_buffer,"Address should begin with",25) ||
  482.        0==strncmp(line_buffer,"<TITLE>Help ",12) ||
  483.        0==strcmp(line_buffer,
  484.                 "Document address invalid or access not authorised"))) {
  485.       if (line_buffer)
  486.       free(line_buffer);
  487.       if (line_kept_clean) 
  488.           free(line_kept_clean);
  489.       extensions = NO;
  490.       already_retrying = 1;
  491.       if (TRACE) fprintf(stderr,
  492.              "HTTP: close socket %d to retry with HTTP0\n", s);
  493.       NETCLOSE(s);
  494.       /* print a progress message */
  495.       _HTProgress ("Retrying as HTTP0 request.");
  496.       goto try_again;
  497.   }
  498.  
  499.  
  500.   {
  501.     int fields;
  502.     char server_version[VERSION_LENGTH+1];
  503.     int server_status;
  504.  
  505.     server_version[0] = 0;
  506.     
  507.     fields = sscanf(line_buffer, "%20s %d",
  508.                     server_version,
  509.                     &server_status);
  510.     
  511.     if (TRACE)
  512.       fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields);
  513.     
  514.     /* Rule out HTTP/1.0 reply as best we can. */
  515.     if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
  516.         server_version[1] != 'T' || server_version[2] != 'T' ||
  517.         server_version[3] != 'P' || server_version[4] != '/' ||
  518.         server_version[6] != '.') 
  519.       {            /* HTTP0 reply */
  520.         HTAtom * encoding;
  521.  
  522.         if (TRACE)
  523.           fprintf (stderr, "--- Talking HTTP0.\n");
  524.         
  525.         format_in = HTFileFormat(arg, &encoding);
  526.     /* treat all plain text as HTML.
  527.          * this sucks but its the only solution without
  528.          * looking at content.
  529.          */
  530.         if(!strncmp(HTAtom_name(format_in), "text/plain",10)) {
  531.             if(TRACE)
  532.                 fprintf(stderr,
  533.                            "HTTP:  format_in being changed to text/HTML\n");
  534.             format_in = WWW_HTML;
  535.         }
  536.  
  537.         start_of_data = line_kept_clean;
  538.       } 
  539.     else 
  540.       {
  541.         /* Decode full HTTP response */
  542.         format_in = HTAtom_for("www/mime");
  543.         /* We set start_of_data to "" when !eol here because there
  544.            will be a put_block done below; we do *not* use the value
  545.            of start_of_data (as a pointer) in the computation of
  546.            length or anything else in this situation. */
  547.         start_of_data = eol ? eol + 1 : "";
  548.         length = eol ? length - (start_of_data - line_buffer) : 0;
  549.         
  550.         if (TRACE)
  551.           fprintf (stderr, "--- Talking HTTP1.\n");
  552.         
  553.         switch (server_status / 100) 
  554.           {
  555.           case 3:        /* Various forms of redirection */
  556.             /*
  557.          * We do not load the file, but read the header for
  558.          * its "Location: " and go get that URL.  If that's
  559.          * another redirecting URL, we'll repeat the fetches
  560.          * until we reach the actual URL. - FM
  561.          */
  562.        {
  563.         char *cp = line_kept_clean;
  564.             doing_redirect = 1;
  565.         if(TRACE)
  566.         fprintf(stderr,"Got Redirect code\n");
  567. once_again:
  568.         /*
  569.          * Look for the "Location: " in the header. - FM
  570.          */
  571.         while (*cp) {
  572.             if(*cp != 'l' && *cp != 'L')
  573.             cp++;
  574.             else if (!strncasecomp(cp, "Location: ", 10)) {
  575.                 extern char *redirecting_url;
  576.                 char *cp1=NULL, *cp2=NULL;
  577.                 cp += 10;
  578.             if((cp1=strchr(cp, LF)) != NULL) {
  579.             int status1;
  580.                 *cp1 = '\0';
  581.                 if((cp2=strchr(cp, CR)) != NULL)
  582.                     *cp2 = '\0';
  583.             /*
  584.              * Load the new URL into redirecting_url,
  585.              * which will serve as a flag for seeking
  586.              * the document at that location. - FM
  587.              */
  588.                 StrAllocCopy(redirecting_url, cp);
  589.                         if (TRACE)
  590.                             fprintf(stderr,
  591.                                 "[HTLoadHTTP] Picked up location '%s'\n", cp);
  592.                 if(cp2)
  593.                     *cp2 = CR;
  594.                 *cp1 = LF;
  595.                 status = HT_REDIRECTING;
  596.             /*
  597.              * Finish reading the redirecting message if
  598.              * we don't have it all yet, then make sure
  599.              * the socket is closed and goto clean_up.
  600.              * Then, we'll check out the redirecting_url
  601.              * and if it's acceptible (e.g., not a telnet
  602.              * URL when we have that disabled) we'll fetch
  603.              * or act on it.
  604.              */
  605.             while (status1=NETREAD(s, line_buffer,
  606.                                INIT_LINE_SIZE)) {
  607.                 if (status1 == HT_INTERRUPTED) {
  608.                     if (TRACE)
  609.                     fprintf (stderr,
  610.                         "HTTP: Interrupted followup read.\n");
  611.                 _HTProgress ("Connection interrupted.");
  612.                 status = HT_INTERRUPTED;
  613.                 NETCLOSE(s);
  614.                 goto clean_up;
  615.                      }
  616.             }
  617.                 NETCLOSE(s);
  618.                 goto clean_up;
  619.             }
  620.                 break;
  621.             }
  622.             else {
  623.                cp++;
  624.             }
  625.         }
  626.         /*
  627.          * If we get to here, we didn't find the "Location: " yet.
  628.          * Get more of the header from the server, and goto
  629.          * "once_again:" to resume searching for it.  We should
  630.          * change this, someday, to get the entire header unfolded
  631.          * at the outset, but this works fine, for now. - FM
  632.          */
  633.         status = NETREAD(s, line_buffer, INIT_LINE_SIZE);
  634.             if (status <= 0) {
  635.                 if (status == HT_INTERRUPTED) {
  636.             /*
  637.              * Impatient user. - FM
  638.              */
  639.                     if (TRACE)
  640.                         fprintf (stderr, "HTTP: Interrupted followup read.\n");
  641.                     _HTProgress ("Connection interrupted.");
  642.                     status = HT_INTERRUPTED;
  643.             NETCLOSE(s);
  644.                     goto clean_up;
  645.                 }
  646.                 else {
  647.             /*
  648.              *  Hmm, no more data, and we didn't find the
  649.              *  redirecting_url.  Better luck next time,
  650.              *  but we'll give up on this request. - FM
  651.              */
  652.                     if (TRACE)
  653.                       fprintf (stderr,
  654.   "HTTP: Hit unexpected network read error; aborting connection; status %d.\n",
  655.                     status);
  656.                     HTAlert(
  657.                  "Unexpected network read error; connection aborted.");
  658.                     NETCLOSE (s);
  659.                     status = HT_NO_DATA;
  660.                     goto clean_up;
  661.                 }
  662.             }
  663.         /*
  664.          *  We got more from the server, so go back and
  665.          *  once_again search for the redirecting_url. - FM
  666.          */
  667.         StrAllocCopy(line_kept_clean, line_buffer);
  668.         cp = line_kept_clean;
  669.         goto once_again;
  670.             break;
  671.        }
  672.             
  673.           case 4:        /* "I think I goofed" */
  674.             switch (server_status) 
  675.               {
  676.               case 403:
  677.                 /* 403 is "forbidden"; display returned text. */
  678.                 /* format_in = HTAtom_for("text/html"); */
  679.                 break;
  680.  
  681.               case 401:
  682.                 /* length -= start_of_data - text_buffer; */
  683.                 if (HTAA_shouldRetryWithAuth(start_of_data, length, s)) 
  684.                   {
  685.                     extern BOOLEAN dump_output_immediately;
  686.  
  687.                     (void)NETCLOSE(s);
  688.                     if (line_buffer) 
  689.                       free(line_buffer);
  690.                     if (line_kept_clean) 
  691.                       free(line_kept_clean);
  692.                     if(dump_output_immediately)    {
  693.                       fprintf(stderr, "HTTP:  Access authorization required.\n");
  694.                       fprintf(stderr, "       Cannot retrieve non-interactively.\n");
  695.                       status = HT_NO_DATA;
  696.                       goto clean_up;
  697.                     }
  698.  
  699.                     if (TRACE) 
  700.                       fprintf(stderr, "%s %d %s\n",
  701.                               "HTTP: close socket", s,
  702.                               "to retry with Access Authorization");
  703.                     
  704.                     _HTProgress ("Retrying with access authorization information.");
  705.                     goto try_again;
  706.                     break;
  707.                   }
  708.                 else 
  709.                   {
  710.                     /* Fall through. */
  711.                   }
  712.  
  713.               default:
  714.                 break;
  715. #if 0
  716.                 char *p1 = HTParse(arg, "", PARSE_HOST);
  717.                 char *message;
  718.                 
  719.                 message = (char*)malloc(strlen(text_buffer) +
  720.                                         strlen(p1) + 100);
  721.                 sprintf(message,
  722.                         "HTTP server at %s replies:\n%s\n\n%s\n",
  723.                         p1, text_buffer,
  724.                         ((server_status == 401) 
  725.                          ? "Access Authorization package giving up.\n"
  726.                          : ""));
  727.                 free(message);
  728.                 free(p1);
  729. #endif
  730. #if 0
  731.                 _HTProgress ("Could not load requested document.");
  732.                 status = -1;
  733.                 goto clean_up;
  734. #endif
  735.               } /* case 4 switch */
  736.             break;
  737.  
  738.           case 5:        /* I think you goofed */
  739.             break;
  740.             
  741.           case 2:        /* Good: Got MIME object */
  742.         if(server_status == 204) {
  743.             status = HT_NO_DATA;
  744.             goto done;
  745.         }
  746.             break;
  747.             
  748.           default:        /* bad number */
  749.             HTAlert("Unknown status reply from server!");
  750.             break;
  751.           } /* Switch on server_status/100 */
  752.         
  753.       }    /* Full HTTP reply */
  754.   } /* scope of fields */
  755.  
  756.   /* Set up the stream stack to handle the body of the message */
  757.   target = HTStreamStack(format_in,
  758.                          format_out,
  759.                          sink, anAnchor);
  760.   
  761.   if (!target || target == NULL) 
  762.     {
  763.       char buffer[1024];    /* @@@@@@@@ */
  764.       sprintf(buffer, "Sorry, no known way of converting %s to %s.",
  765.               HTAtom_name(format_in), HTAtom_name(format_out));
  766.       _HTProgress (buffer);
  767.       status = -1;
  768.       goto clean_up;
  769.     }
  770.  
  771.   /* Recycle the first chunk of data, in all cases. */
  772.   (*target->isa->put_block)(target, start_of_data, length);
  773.  
  774.   /* Go pull the bulk of the data down. */
  775.   rv = HTCopy(s, target);
  776.  
  777.   if (rv == -1)
  778.     {
  779.       /* Intentional interrupt before data were received, not an error */
  780.       /* (*target->isa->_abort)(target, NULL);/* already done in HTCopy */
  781.       status = HT_INTERRUPTED;
  782.       NETCLOSE(s);
  783.       goto clean_up;
  784.     }
  785.   if (rv == -2 && !already_retrying && !do_post)
  786.     { 
  787.       /* Aw hell, a REAL error, maybe cuz it's a dumb HTTP0 server */
  788.       if (TRACE)
  789.         fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n");
  790.       /* May as well consider it an interrupt -- right? */
  791.       (*target->isa->_abort)(target, NULL);
  792.       NETCLOSE(s);
  793.       if (line_buffer) 
  794.         free(line_buffer);
  795.       if (line_kept_clean) 
  796.         free(line_kept_clean);
  797.  
  798.       extensions = NO;
  799.       already_retrying = 1;
  800.       _HTProgress ("Retrying as HTTP0 request.");
  801.       goto try_again;
  802.     }
  803.  
  804.   /* 
  805.    * Close socket if partial transmission (was freed on abort)
  806.    * Free if complete transmission (socket was closed before return)
  807.    */
  808.   if (rv == HT_INTERRUPTED)
  809.       NETCLOSE(s);
  810.   else
  811.       (*target->isa->_free)(target);
  812.  
  813.   if (doing_redirect)
  814.     /*
  815.      * We already jumped over all this if the "case 3:" code worked
  816.      * above, but we'll check here as a backup in case it fails. - FM
  817.      */
  818.     {
  819.       /* Lou's old comment:  - FM */
  820.       /* OK, now we've got the redirection URL temporarily stored
  821.          in external variable redirecting_url, exported from HTMIME.c,
  822.          since there's no straightforward way to do this in the library
  823.          currently.  Do the right thing. */
  824.       status = HT_REDIRECTING;
  825.     }
  826.   else
  827.     {
  828.       /* If any data were received, treat as a complete transmission */
  829.       status = HT_LOADED;
  830.     }
  831.  
  832.   /*    Clean up
  833.    */
  834.   
  835. clean_up: 
  836.   if (line_buffer) 
  837.     free(line_buffer);
  838.   if (line_kept_clean) 
  839.     free(line_kept_clean);
  840.  
  841.  done:
  842.   /* Clear out on exit, just in case. */
  843.   do_post = 0;
  844.   return status;
  845. }
  846.  
  847.  
  848. /*    Protocol descriptor
  849. */
  850.  
  851. GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0 };
  852. GLOBALDEF PUBLIC HTProtocol HTTPS = { "https", HTLoadHTTP, 0 };
  853.